//go:build windows
// +build windows

package win防火墙类

import (
	"fmt"
	"log"
	"os"
	"runtime"

	"gitee.com/go_888/internal-call/comshim"
	ole "github.com/go-ole/go-ole"
	"github.com/go-ole/go-ole/oleutil"
)

/*
注意!!!  请以管理员身份运行!
这个包来源: https://github.com/iamacarpet/go-win64api
*/

// 与防火墙相关的API常量。
const (
	X常量_协议_TCP    = 6
	X常量_协议_UDP    = 17
	X常量_协议_ICMPv4 = 1
	X常量_协议_ICMPv6 = 58
	X常量_协议_全部   = 256

	X常量_方向_入站 = 1
	X常量_方向_出站 = 2

	X常量_出入站处理方式_阻止 = 0 //
	X常量_出入站处理方式_允许 = 1 //

	// X常量_配置文件_CURRENT 并不是一个真实的API常量，而只是一个在防火墙函数中使用的辅助项。
	// 它可以表示一个配置文件或者多个（甚至全部）配置文件。这取决于当前正在使用的配置文件是哪一个。
	// 每个活跃的接口都可以有自己的配置文件。例如：公共配置用于Wi-Fi，域配置用于VPN，私有配置用于局域网。
	// 同时存在所有这些配置
	X常量_配置文件_CURRENT = 0
	X常量_配置文件_域      = 1
	X常量_配置文件_专用    = 2
	X常量_配置文件_公用    = 4
	X常量_配置文件_全部    = 2147483647
)

// X配置文件 表示当前激活的防火墙配置文件（们）。
type X配置文件 struct {
	X域, X专用, X公用 bool
}

// FWRule代表防火墙规则。
type X防火墙规则 struct {
	X规则名称, X描述, X程序路径, X服务名称 string
	X本地端口, X远程端口                   string

	// 本地地址 和 远程地址 总是会以子网掩码的形式返回，例如：
	// `10.10.1.1/255.255.255.0`
	X本地地址, X远程地址 string

	// ICMP类型和代码 是一个字符串。你可以在这里定义多个由 ":"（冒号）分隔的代码。
	// 类型列表见：
	// https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml
	// 要允许 ping，应将其设置为：
	// "0"
	ICMP类型和代码 string
	X组            string //

	// X接口类型 可以是：
	// "LAN", "Wireless", "RemoteAccess", "All"
	// 您可以使用逗号添加多个，例如：
	// "LAN, Wireless"
	X接口类型                          string
	X协议, X方向, X处理方式, X配置文件 int32 //
	X启用, EdgeTraversal               bool  //
}

func isAdmin() bool {
	_, err := os.Open(`\\.\PHYSICALDRIVE0`)
	if err != nil {
		return false
	}
	return true
}

// X取配置文件状态 返回FWProfiles结构体，这样你可以检查规则在哪个配置文件中生效。
// 作为替代方案，你也可以分析FWRule.Profile的值。
func (r *X防火墙规则) X取配置文件状态() X配置文件 {
	if !isAdmin() {
		log.Printf("错误: 请以管理员身份运行!")
	}
	if r.X配置文件 == X常量_配置文件_全部 {
		return X配置文件{true, true, true}
	}
	return firewallParseProfiles(r.X配置文件)
}

// X添加入站规则 为给定的端口或端口范围创建入站规则。
// 规则名称 是必需的，并且不能包含 "|" 字符。
// 描述和组 是可选的。描述中也不能包含 "|" 字符。
// 端口（-s）是必需的。 如： "5800, 5900, 6810-6812"
// 协议通常会是： X常量_协议_TCP 或者 X常量_协议_UDP
//
// 配置文件（Profile）将决定该规则适用于哪些配置文件。你可以使用：
// X常量_配置文件_CURRENT // 将规则添加到当前使用的防火墙配置文件
// X常量_配置文件_全部 // 将规则添加到所有配置文件
// X常量_配置文件_域 | X常量_配置文件_专用 // 在私有和域配置文件中应用规则
func X添加入站规则(规则名称, 可选描述, 可选分组, 端口 string, X常量_协议_, X常量_配置文件_ int32) (bool, error) {
	if !isAdmin() {
		log.Printf("错误: 请以管理员身份运行!")
	}
	if 端口 == "" {
		return false, fmt.Errorf("错误: 规则端口为空，这是强制性的")
	}
	return firewallRuleAdd(规则名称, 可选描述, 可选分组, "", "", 端口, "", "", "", "", X常量_协议_, 0, X常量_出入站处理方式_允许, X常量_配置文件_, true, false)
}

// X添加应用程序入站规则 为给定的应用程序创建入站规则。
// 规则名称 是必需的，并且不能包含 "|" 字符。
// 描述和组 是可选的。描述同样不能包含 "|" 字符。
// exe路径 是必需的。 如：`%ProgramFiles% (x86)\RemoteControl\winvnc.exe`
//
// 策略将决定规则应用于哪些配置文件。您可以使用：
// X常量_配置文件_CURRENT // 将规则添加到当前使用的防火墙配置文件
// X常量_配置文件_全部 // 将规则添加到所有配置文件
// X常量_配置文件_域|X常量_配置文件_专用 // 在私有和域配置文件中应用规则
func X添加应用程序入站规则(规则名称, 可选描述, 可选分组, exe路径 string, X常量_配置文件_ int32) (bool, error) {
	if !isAdmin() {
		log.Printf("错误: 请以管理员身份运行!")
	}
	if exe路径 == "" {
		return false, fmt.Errorf("错误: 规则<exe路径>为空，它是必需的")
	}
	return firewallRuleAdd(规则名称, 可选描述, 可选分组, exe路径, "", "", "", "", "", "", 0, 0, X常量_出入站处理方式_允许, X常量_配置文件_, true, false)
}

// X添加应用程序出站规则 为给定的应用程序创建出站规则。
// 规则名称 是必需的，并且不能包含 "|" 字符。
// 描述和组 是可选的。描述同样不能包含 "|" 字符。
// exe路径 是必需的。 如：`%ProgramFiles% (x86)\RemoteControl\winvnc.exe`
//
// 策略将决定规则应用于哪些配置文件。您可以使用：
// X常量_配置文件_CURRENT // 将规则添加到当前使用的防火墙配置文件
// X常量_配置文件_全部 // 将规则添加到所有配置文件
// X常量_配置文件_域|X常量_配置文件_专用 // 在私有和域配置文件中应用规则
func X添加应用程序出站规则(规则名称, 可选描述, 可选分组, exe路径 string, X常量_配置文件_ int32) (bool, error) {
	if !isAdmin() {
		log.Printf("错误: 请以管理员身份运行!")
	}
	if exe路径 == "" {
		return false, fmt.Errorf("错误: 规则<exe路径>为空，它是必需的")
	}
	return firewallRuleAdd(规则名称, 可选描述, 可选分组, exe路径, "", "", "", "", "", "", 0, X常量_方向_出站, X常量_出入站处理方式_允许, X常量_配置文件_, true, false)
}

// X启用防火墙ping 创建一个允许响应回显请求的入站 ICMPv4 规则。
// 规则名称是必需的，并且不能包含 "|" 字符。
// 描述和组是可选的。描述中也不能包含 "|" 字符。
// 远程地址:允许你限制特定 IP 地址范围内的 Ping 请求，例如：
// "10.10.10.0/24"
// 这将会被内部转换为：
// "10.10.10.0/255.255.255.0"
//
// Profile 将决定规则应用到哪些配置文件中。你可以使用以下选项：
// X常量_配置文件_CURRENT // 将规则添加到当前使用的防火墙配置文件
// X常量_配置文件_全部 // 将规则添加到所有配置文件
// X常量_配置文件_域|X常量_配置文件_专用 // 在私有和域配置文件中应用规则
func X启用防火墙ping(规则名称, 可选描述, 可选分组, 远程地址 string, 配置文件 int32) (bool, error) {
	if !isAdmin() {
		log.Printf("错误: 请以管理员身份运行!")
	}
	return firewallRuleAdd(规则名称, 可选描述, 可选分组, "", "", "", "", "", 远程地址, "8:*", X常量_协议_ICMPv4, 0, X常量_出入站处理方式_允许, 配置文件, true, false)
}

// X底层_添加防火墙规则 允许修改几乎所有的FW规则参数。你可能不希望使用这个功能，因为该函数允许创建任何规则，甚至在给定的配置文件中打开所有端口。因此，请谨慎使用。
// 提示：使用FirewallRulesGet 获取如何定义规则的示例。
// 备注:不建议直接调用这个.这是底层方法
func X底层_添加防火墙规则(规则 X防火墙规则) (bool, error) {
	if !isAdmin() {
		log.Printf("错误: 请以管理员身份运行!")
	}
	return firewallRuleAdd(规则.X规则名称, 规则.X描述, 规则.X组, 规则.X程序路径, 规则.X服务名称,
		规则.X本地端口, 规则.X远程端口, 规则.X本地地址, 规则.X远程地址, 规则.ICMP类型和代码,
		规则.X协议, 规则.X方向, 规则.X处理方式, 规则.X配置文件, 规则.X启用, 规则.EdgeTraversal)
}

// X删除规则 允许您通过名称删除现有的规则。如果存在多个同名规则，将随机删除第一个（如果有）。您可以运行此函数进行循环以移除所有规则：
//
//	var err error
//	for {
//	    if ok, err := wapi.X删除规则("anydesk.exe"); !ok || err != nil {
//	        break
//	    }
//	 }
//	 如果出现错误，则将其打印出来： if err != nil { fmt.Println(err) }
func X删除规则(规则名称 string) (bool, error) {
	if !isAdmin() {
		log.Printf("错误: 请以管理员身份运行!")
	}
	if 规则名称 == "" {
		return false, fmt.Errorf("空FW规则:规则名称，规则名称是强制性的")
	}

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	u, fwPolicy, err := firewallAPIInit()
	if err != nil {
		return false, err
	}
	defer firewallAPIRelease(u, fwPolicy)

	unknownRules, err := oleutil.GetProperty(fwPolicy, "Rules")
	if err != nil {
		return false, fmt.Errorf("获取规则失败：%s", err)
	}
	rules := unknownRules.ToIDispatch()

	if ok, err := firewallRuleExistsByName(rules, 规则名称); err != nil {
		return false, fmt.Errorf("检查重复规则时出错：%s", err)
	} else if !ok {
		return false, nil
	}

	if _, err := oleutil.CallMethod(rules, "Remove", 规则名称); err != nil {
		return false, fmt.Errorf("删除规则时出错：%s", err)
	}
	return true, nil
}

// X取所有防火墙规则 返回防火墙中定义的所有规则。
func X取所有防火墙规则() ([]X防火墙规则, error) {
	if !isAdmin() {
		log.Printf("错误: 请以管理员身份运行!")
	}
	rules := make([]X防火墙规则, 0, 1024)

	u, fwPolicy, err := firewallAPIInit()
	if err != nil {
		return rules, err
	}
	defer firewallAPIRelease(u, fwPolicy)

	ur, ep, enum, err := firewallRulesEnum(fwPolicy)
	if err != nil {
		return rules, err
	}
	defer firewallRulesEnumRelease(ur, ep, enum)

	for itemRaw, length, err := enum.Next(1); length > 0; itemRaw, length, err = enum.Next(1) {
		if err != nil {
			return rules, fmt.Errorf("无法查找下一个规则项：%s", err)
		}

		rule, err := firewallRuleParams(itemRaw)
		if err != nil {
			return rules, err
		}
		rules = append(rules, rule)
	}

	return rules, nil
}

func firewallRuleName(item *ole.IDispatch) (string, error) {
	name, err := oleutil.GetProperty(item, "Name")
	if err != nil {
		return "", fmt.Errorf("无法获取规则的属性（名称），错误：%v", err)
	}
	return name.ToString(), nil
}

// firewallRuleParams 从API检索所有规则参数并将其保存在FWRule结构体中。
func firewallRuleParams(itemRaw ole.VARIANT) (X防火墙规则, error) {
	var rule X防火墙规则
	item := itemRaw.ToIDispatch()
	defer item.Release()

	var err error
	rule.X规则名称, err = getStringProperty(item, "Name")
	if err != nil {
		return rule, fmt.Errorf("无法获取规则的属性（名称）")
	}
	rule.X描述, err = getStringProperty(item, "Description")
	if err != nil {
		return rule, fmt.Errorf("无法获取规则%q的属性（描述）", rule.X规则名称)
	}
	rule.X程序路径, err = getStringProperty(item, "ApplicationName")
	if err != nil {
		return rule, fmt.Errorf("无法获取规则%q的属性（ApplicationName）", rule.X规则名称)
	}
	rule.X服务名称, err = getStringProperty(item, "ServiceName")
	if err != nil {
		return rule, fmt.Errorf("无法获取规则%q的属性（ServiceName）", rule.X规则名称)
	}
	rule.X本地端口, err = getStringProperty(item, "LocalPorts")
	if err != nil {
		return rule, fmt.Errorf("无法获取规则%q的属性（LocalPorts）", rule.X规则名称)
	}

	rule.X远程端口, err = getStringProperty(item, "RemotePorts")
	if err != nil {
		return rule, fmt.Errorf("无法获取规则%q的属性（RemotePorts）", rule.X规则名称)
	}
	rule.X本地地址, err = getStringProperty(item, "LocalAddresses")
	if err != nil {
		return rule, fmt.Errorf("无法获取规则%q的属性（本地地址）", rule.X规则名称)
	}
	rule.X远程地址, err = getStringProperty(item, "RemoteAddresses")
	if err != nil {
		return rule, fmt.Errorf("无法获取规则%q的属性（远程地址）", rule.X规则名称)
	}
	rule.ICMP类型和代码, err = getStringProperty(item, "ICMPTypesAndCodes")
	if err != nil {
		return rule, fmt.Errorf("无法获取规则%q的属性（ICMPTypesAndCodes）", rule.X规则名称)
	}
	rule.X组, err = getStringProperty(item, "Grouping")
	if err != nil {
		return rule, fmt.Errorf("无法获取规则%q的属性（分组）", rule.X规则名称)
	}
	rule.X接口类型, err = getStringProperty(item, "InterfaceTypes")
	if err != nil {
		return rule, fmt.Errorf("无法获取规则%q的属性（InterfaceTypes）", rule.X规则名称)
	}
	rule.X协议, err = getInt32Property(item, "Protocol")
	if err != nil {
		return rule, fmt.Errorf("无法获取规则%q的属性（协议）", rule.X规则名称)
	}
	rule.X方向, err = getInt32Property(item, "Direction")
	if err != nil {
		return rule, fmt.Errorf("无法获取规则%q的属性（方向）", rule.X规则名称)
	}
	rule.X处理方式, err = getInt32Property(item, "Action")
	if err != nil {
		return rule, fmt.Errorf("无法获取规则%q的属性（操作）", rule.X规则名称)
	}
	rule.X启用, err = getBoolProperty(item, "Enabled")
	if err != nil {
		return rule, fmt.Errorf("无法获取规则%q的属性（已启用）", rule.X规则名称)
	}
	rule.EdgeTraversal, err = getBoolProperty(item, "EdgeTraversal")
	if err != nil {
		return rule, fmt.Errorf("无法获取规则%q的属性（EdgeTraversal）", rule.X规则名称)
	}
	rule.X配置文件, err = getInt32Property(item, "Profiles")
	if err != nil {
		return rule, fmt.Errorf("无法获取规则%q的属性（配置文件）", rule.X规则名称)
	}

	return rule, nil
}
func getInt32Property(dispatch *ole.IDispatch, property string) (int32, error) {
	val, err := oleutil.GetProperty(dispatch, property)
	if err != nil {
		log.Printf("无法获取调度属性：%s \n", err.Error())
		return 0, err
	}
	defer val.Clear()
	return val.Value().(int32), nil
}

func getStringProperty(dispatch *ole.IDispatch, property string) (string, error) {
	val, err := oleutil.GetProperty(dispatch, property)
	if err != nil {
		log.Printf("无法获取调度属性：%s \n", err.Error())
		return "", err
	}
	defer val.Clear()
	return val.ToString(), nil
}

func getBoolProperty(dispatch *ole.IDispatch, property string) (bool, error) {
	val, err := oleutil.GetProperty(dispatch, property)
	if err != nil {
		log.Printf("无法获取调度属性：%s \n", err.Error())
		return false, err
	}
	defer val.Clear()
	return val.Value().(bool), nil
}

// X启用防火墙组 允许启用预定义的防火墙组。最好不要使用如 "文件和打印机共享" 这样的名称，因为它们是本地化的，你的函数在非英文的Windows上将无法正常工作。
// 可以参考 FILE_AND_PRINTER_SHARING 常量作为示例。
// 你可以在这里找到这段代码：
// https://windows10dll.nirsoft.net/firewallapi_dll.html
//
// 你可以在此选择的FW配置文件中启用组，或者在当前的配置文件中启用，可以使用类似这样的方式：
// X常量_配置文件_域 | X常量_配置文件_专用
//
// 来在指定的配置文件中启用组。
func X启用防火墙组(组名称 string, 配置文件 int32) error {
	if !isAdmin() {
		log.Printf("错误: 请以管理员身份运行!")
	}
	return firewallGroup(组名称, 配置文件, true)
}

// FirewallGroupDisable在给定配置文件中禁用指定的组。这与FirewallGroupEnable的规则相同。
func X禁用防火墙组(组名称 string, 配置文件 int32) error {
	if !isAdmin() {
		log.Printf("错误: 请以管理员身份运行!")
	}
	return firewallGroup(组名称, 配置文件, false)
}

func firewallGroup(组名称 string, 配置文件 int32, enable bool) error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	u, fwPolicy, err := firewallAPIInit()
	if err != nil {
		return err
	}
	defer firewallAPIRelease(u, fwPolicy)

	if 配置文件 == X常量_配置文件_CURRENT {
		currentProfiles, err := oleutil.GetProperty(fwPolicy, "CurrentProfileTypes")
		if err != nil {
			return fmt.Errorf("无法获取当前配置文件：%s", err)
		}
		配置文件 = currentProfiles.Value().(int32)
	}

	if _, err := oleutil.CallMethod(fwPolicy, "EnableRuleGroup", 配置文件, 组名称, enable); err != nil { //currentProfiles
		return fmt.Errorf("启用组%s时出错", err)
	}

	return nil
}

// X防火墙是否已启用 在给定配置文件启用防火墙时返回 true。
// 你可以使用所有 NET_FW_PROFILE2* 常量，但不包括 X常量_配置文件_全部。
// 如果无法检查防火墙状态，它将返回错误。
func X防火墙是否已启用(配置文件 int32) (bool, error) {
	if !isAdmin() {
		log.Printf("错误: 请以管理员身份运行!")
	}
	u, fwPolicy, err := firewallAPIInit()
	if err != nil {
		return false, err
	}
	defer firewallAPIRelease(u, fwPolicy)

	switch 配置文件 {
	case X常量_配置文件_CURRENT:
		currentProfiles, err := oleutil.GetProperty(fwPolicy, "CurrentProfileTypes")
		if err != nil {
			return false, fmt.Errorf("无法获取当前配置文件：%s", err)
		}
		配置文件 = currentProfiles.Value().(int32)
	case X常量_配置文件_全部:
		return false, fmt.Errorf("不能将NET_FW_PROFILE2_ALL用作参数")
	}

	enabled, err := oleutil.GetProperty(fwPolicy, "FirewallEnabled", 配置文件)

	return enabled.Value().(bool), err
}

// FirewallEnable功能是为给定的配置文件启用防火墙。
// 如果配置文件已经启用了防火墙，它将返回false。
func X开启防火墙(配置文件 int32) (bool, error) {
	if !isAdmin() {
		log.Printf("错误: 请以管理员身份运行!")
	}
	u, fwPolicy, err := firewallAPIInit()
	if err != nil {
		return false, err
	}
	defer firewallAPIRelease(u, fwPolicy)

	enabled, err := oleutil.GetProperty(fwPolicy, "FirewallEnabled", 配置文件)
	if err != nil {
		return false, err
	}
	if enabled.Value().(bool) {
		return false, nil
	}

	_, err = oleutil.PutProperty(fwPolicy, "FirewallEnabled", 配置文件, true)
	if err != nil {
		return false, err
	}
	return true, nil
}

// FirewallDisable禁用给定配置文件的防火墙。
// 如果配置文件中已经禁用了防火墙，它将返回false。
func X禁用防火墙(配置文件 int32) (bool, error) {
	if !isAdmin() {
		log.Printf("错误: 请以管理员身份运行!")
	}
	u, fwPolicy, err := firewallAPIInit()
	if err != nil {
		return false, err
	}
	defer firewallAPIRelease(u, fwPolicy)

	enabled, err := oleutil.GetProperty(fwPolicy, "FirewallEnabled", 配置文件)
	if err != nil {
		return false, err
	}
	if !enabled.Value().(bool) {
		return false, nil
	}

	_, err = oleutil.PutProperty(fwPolicy, "FirewallEnabled", 配置文件, false)
	if err != nil {
		return false, err
	}
	return true, nil
}

// X取防火墙出站默认处理方式 检查未匹配规则的出站连接是被允许还是被阻止。
// 返回值为 X常量_出入站处理方式_允许 或 X常量_出入站处理方式_阻止 之一。
func X取防火墙出站默认处理方式(配置文件 int32) (int32, error) {
	if !isAdmin() {
		log.Printf("错误: 请以管理员身份运行!")
	}
	u, fwPolicy, err := firewallAPIInit()
	if err != nil {
		return 0, err
	}
	defer firewallAPIRelease(u, fwPolicy)

	action, err := oleutil.GetProperty(fwPolicy, "DefaultOutboundAction", 配置文件)
	if err != nil {
		return 0, err
	}
	return action.Value().(int32), nil
}

// X取防火墙入站默认处理方式 检查未匹配规则的入站连接是允许还是被阻止。
// 返回NET_FW_ACTION_ALLOW（允许）或NET_FW_ACTION_BLOCK（阻止）。
func X取防火墙入站默认处理方式(配置文件 int32) (int32, error) {
	if !isAdmin() {
		log.Printf("错误: 请以管理员身份运行!")
	}
	u, fwPolicy, err := firewallAPIInit()
	if err != nil {
		return 0, err
	}
	defer firewallAPIRelease(u, fwPolicy)

	action, err := oleutil.GetProperty(fwPolicy, "DefaultInboundAction", 配置文件)
	if err != nil {
		return 0, err
	}
	return action.Value().(int32), nil
}

// X设置防火墙出站默认处理方式 设置对外连接的默认策略。
// action 必须是NET_FW_ACTION_ALLOW（允许）或NET_FW_ACTION_BLOCK（阻止）。
func X设置防火墙出站默认处理方式(配置文件, 处理方式 int32) error {
	if !isAdmin() {
		log.Printf("错误: 请以管理员身份运行!")
	}
	u, fwPolicy, err := firewallAPIInit()
	if err != nil {
		return err
	}
	defer firewallAPIRelease(u, fwPolicy)

	_, err = oleutil.PutProperty(fwPolicy, "DefaultOutboundAction", 配置文件, 处理方式)
	return err
}

// X设置防火墙入站默认处理方式 设置传入连接的默认策略。
// action 必须是 X常量_出入站处理方式_允许 或 X常量_出入站处理方式_阻止。
func X设置防火墙入站默认处理方式(配置文件, 处理方式 int32) error {
	if !isAdmin() {
		log.Printf("错误: 请以管理员身份运行!")
	}
	u, fwPolicy, err := firewallAPIInit()
	if err != nil {
		return err
	}
	defer firewallAPIRelease(u, fwPolicy)

	_, err = oleutil.PutProperty(fwPolicy, "DefaultInboundAction", 配置文件, 处理方式)
	return err
}

// X取配置文件状态 返回当前激活的配置文件。每个激活的接口都可以有自己的配置文件。例如：公共配置用于Wi-Fi，域配置用于VPN，私有配置用于局域网。同时可以全部存在。
func X取配置文件状态() (X配置文件, error) {
	if !isAdmin() {
		log.Printf("错误: 请以管理员身份运行!")
	}
	u, fwPolicy, err := firewallAPIInit()
	if err != nil {
		return X配置文件{}, err
	}
	defer firewallAPIRelease(u, fwPolicy)
	currentProfiles, err := oleutil.GetProperty(fwPolicy, "CurrentProfileTypes")
	if err != nil {
		return X配置文件{}, fmt.Errorf("无法获取FW当前配置文件：%s", err)
	}

	cp := firewallParseProfiles(currentProfiles.Value().(int32))

	if !(cp.X域 || cp.X专用 || cp.X公用) {
		// 是否根本不可能有激活的配置？没有网络吗？
		return cp, fmt.Errorf("未检测到活动的FW配置文件")
	}

	return cp, nil
}

// firewallParseProfiles 返回一个 X配置文件 结构体，
// 该结构体保存了给定整数对应的哪些配置文件已启用。
func firewallParseProfiles(v int32) X配置文件 {
	var p X配置文件
	if v&X常量_配置文件_域 != 0 {
		p.X域 = true
	}
	if v&X常量_配置文件_专用 != 0 {
		p.X专用 = true
	}
	if v&X常量_配置文件_公用 != 0 {
		p.X公用 = true
	}
	return p
}

// firewallRuleAdd 是一个通用函数，用于添加各种规则。
func firewallRuleAdd(规则名称, 描述, 组, 程序路径, serviceName, 本地端口, 远程端口, 本地地址, 远程地址, icmpTypes string, 协议, 方向, action, 配置文件 int32, 启用, edgeTraversal bool) (bool, error) {

	if 规则名称 == "" {
		return false, fmt.Errorf("FW规则名称为空，名称为必填项")
	}

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	u, fwPolicy, err := firewallAPIInit()
	if err != nil {
		return false, err
	}
	defer firewallAPIRelease(u, fwPolicy)

	if 配置文件 == X常量_配置文件_CURRENT {
		currentProfiles, err := oleutil.GetProperty(fwPolicy, "CurrentProfileTypes")
		if err != nil {
			return false, fmt.Errorf("无法获取当前配置文件：%s", err)
		}
		配置文件 = currentProfiles.Value().(int32)
	}
	unknownRules, err := oleutil.GetProperty(fwPolicy, "Rules")
	if err != nil {
		return false, fmt.Errorf("获取规则失败：%s", err)
	}
	rules := unknownRules.ToIDispatch()

	if ok, err := firewallRuleExistsByName(rules, 规则名称); err != nil {
		return false, fmt.Errorf("检查重复规则时出错：%s", err)
	} else if ok {
		return false, nil
	}

	unknown2, err := oleutil.CreateObject("HNetCfg.FWRule")
	if err != nil {
		return false, fmt.Errorf("创建规则对象时出错：%s", err)
	}
	defer unknown2.Release()

	fwRule, err := unknown2.QueryInterface(ole.IID_IDispatch)
	if err != nil {
		return false, fmt.Errorf("创建规则对象（2）时出错：%s", err)
	}
	defer fwRule.Release()

	if _, err := oleutil.PutProperty(fwRule, "Name", 规则名称); err != nil {
		return false, fmt.Errorf("设置规则的属性（名称）时出错：%s", err)
	}
	if _, err := oleutil.PutProperty(fwRule, "Description", 描述); err != nil {
		return false, fmt.Errorf("设置规则的属性（描述）时出错：%s", err)
	}
	if 程序路径 != "" {
		if _, err := oleutil.PutProperty(fwRule, "Applicationname", 程序路径); err != nil {
			return false, fmt.Errorf("设置规则%s的属性（应用程序名）时出错", err)
		}
	}
	if serviceName != "" {
		if _, err := oleutil.PutProperty(fwRule, "ServiceName", serviceName); err != nil {
			return false, fmt.Errorf("设置规则的属性（ServiceName）时出错：%s", err)
		}
	}
	if 协议 != 0 {
		if _, err := oleutil.PutProperty(fwRule, "Protocol", 协议); err != nil {
			return false, fmt.Errorf("设置规则的属性（协议）时出错：%s", err)
		}
	}
	if icmpTypes != "" {
		if _, err := oleutil.PutProperty(fwRule, "IcmpTypesAndCodes", icmpTypes); err != nil {
			return false, fmt.Errorf("设置规则的属性（IcmpTypesAndCodes）时出错：%s", err)
		}
	}
	if 本地端口 != "" {
		if _, err := oleutil.PutProperty(fwRule, "LocalPorts", 本地端口); err != nil {
			return false, fmt.Errorf("设置规则%s的属性（LocalPorts）时出错", err)
		}
	}
	if 远程端口 != "" {
		if _, err := oleutil.PutProperty(fwRule, "RemotePorts", 远程端口); err != nil {
			return false, fmt.Errorf("设置规则%s的属性（RemotePorts）时出错", err)
		}
	}
	if 本地地址 != "" {
		if _, err := oleutil.PutProperty(fwRule, "LocalAddresses", 本地地址); err != nil {
			return false, fmt.Errorf("设置规则%s的属性（LocalAddresses）时出错", err)
		}
	}
	if 远程地址 != "" {
		if _, err := oleutil.PutProperty(fwRule, "RemoteAddresses", 远程地址); err != nil {
			return false, fmt.Errorf("设置规则%s的属性（RemoteAddresses）时出错", err)
		}
	}
	if 方向 != 0 {
		if _, err := oleutil.PutProperty(fwRule, "Direction", 方向); err != nil {
			return false, fmt.Errorf("设置规则的属性（方向）时出错：%s", err)
		}
	}
	if _, err := oleutil.PutProperty(fwRule, "Enabled", 启用); err != nil {
		return false, fmt.Errorf("设置规则%s的属性（已启用）时出错", err)
	}
	if _, err := oleutil.PutProperty(fwRule, "Grouping", 组); err != nil {
		return false, fmt.Errorf("设置规则的属性（分组）时出错：%s", err)
	}
	if _, err := oleutil.PutProperty(fwRule, "Profiles", 配置文件); err != nil {
		return false, fmt.Errorf("设置规则%s的属性（配置文件）时出错", err)
	}
	if _, err := oleutil.PutProperty(fwRule, "Action", action); err != nil {
		return false, fmt.Errorf("设置规则的属性（操作）时出错：%s", err)
	}
	if edgeTraversal {
		if _, err := oleutil.PutProperty(fwRule, "EdgeTraversal", edgeTraversal); err != nil {
			return false, fmt.Errorf("设置规则的属性（EdgeTraversal）时出错：%s", err)
		}
	}

	if _, err := oleutil.CallMethod(rules, "Add", fwRule); err != nil {
		return false, fmt.Errorf("添加规则时出错：%s", err)
	}

	return true, nil
}

func firewallRuleExistsByName(rules *ole.IDispatch, name string) (bool, error) {
	enumProperty, err := rules.GetProperty("_NewEnum")
	if err != nil {
		return false, fmt.Errorf("无法获取规则的枚举属性：%s", err)
	}
	defer enumProperty.Clear()

	enum, err := enumProperty.ToIUnknown().IEnumVARIANT(ole.IID_IEnumVariant)
	if err != nil {
		return false, fmt.Errorf("无法将枚举强制转换为正确的类型：%s", err)
	}
	if enum == nil {
		return false, fmt.Errorf("无法获取IEnumVARIANT，枚举为nil")
	}

	for itemRaw, length, err := enum.Next(1); length > 0; itemRaw, length, err = enum.Next(1) {
		if err != nil {
			return false, fmt.Errorf("无法查找下一个规则项：%s", err)
		}

		t, err := func() (bool, error) {
			item := itemRaw.ToIDispatch()
			defer item.Release()

			if item, err := oleutil.GetProperty(item, "Name"); err != nil {
				return false, fmt.Errorf("无法获取规则的属性（名称）")
			} else if item.ToString() == name {
				return true, nil
			}

			return false, nil
		}()

		if err != nil {
			return false, err
		} else if t {
			return true, nil
		}
	}

	return false, nil
}

// firewallRulesEnum 接受一个fwPolicy对象，并返回所有需要释放的对象以及枚举本身，用于遍历规则。请不要忘记：
//
//	使用defer关键字调用firewallRulesEnumRelease(ur, ep)
func firewallRulesEnum(fwPolicy *ole.IDispatch) (*ole.VARIANT, *ole.VARIANT, *ole.IEnumVARIANT, error) {
	unknownRules, err := oleutil.GetProperty(fwPolicy, "Rules")
	if err != nil {
		return nil, unknownRules, nil, fmt.Errorf("无法获取规则：%s", err)
	}
	rules := unknownRules.ToIDispatch()

	enumProperty, err := rules.GetProperty("_NewEnum")
	if err != nil {
		unknownRules.Clear()
		return nil, unknownRules, nil, fmt.Errorf("无法获取规则%s的枚举属性", err)
	}

	enum, err := enumProperty.ToIUnknown().IEnumVARIANT(ole.IID_IEnumVariant)
	if err != nil {
		enumProperty.Clear()
		unknownRules.Clear()
		return nil, unknownRules, nil, fmt.Errorf("无法将枚举强制转换为正确的类型：%s", err)
	}
	if enum == nil {
		enumProperty.Clear()
		unknownRules.Clear()
		return nil, unknownRules, nil, fmt.Errorf("无法获取IEnumVARIANT，枚举为nil")
	}
	return unknownRules, enumProperty, enum, nil
}

// firewallRuleEnumRelease 将释放 firewallRulesEnum 占用的内存。
func firewallRulesEnumRelease(unknownRules, enumProperty *ole.VARIANT, enum *ole.IEnumVARIANT) {
	enumProperty.Clear()
	unknownRules.Clear()
	enum.Release()
}

// firewallAPIInit 初始化通用的fw api。
// 然后：
// 分发firewallAPIRelease(u, fwp)
func firewallAPIInit() (*ole.IUnknown, *ole.IDispatch, error) {
	comshim.Add(1)

	unknown, err := oleutil.CreateObject("HNetCfg.FwPolicy2")
	if err != nil {
		return nil, nil, fmt.Errorf("创建FwPolicy对象失败：%s", err)
	}

	fwPolicy, err := unknown.QueryInterface(ole.IID_IDispatch)
	if err != nil {
		unknown.Release()
		return nil, nil, fmt.Errorf("创建FwPolicy对象（2）失败：%s", err)
	}

	return unknown, fwPolicy, nil

}

// firewallAPIRelease 清理内存。
func firewallAPIRelease(u *ole.IUnknown, fwp *ole.IDispatch) {
	fwp.Release()
	u.Release()
	comshim.Done()
}
